package gorm_v2

import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    gormLog "gorm.io/gorm/logger"
    "gorm.io/plugin/dbresolver"
    "time"
    "wwgin/config"
)

// 获取数据库驱动, 可以通过options 动态参数连接任意多个数据库
// param db sting 数据库配置参考配置
func GetSqlDriver(db string) (*gorm.DB, error) {
    //默认模式,不开启读写分量
    gormDb, err := gorm.Open(mysql.Open(getMasterDsn(db)), &gorm.Config{
        SkipDefaultTransaction: true,
        PrepareStmt:            true,
        Logger:                 redefineLog(), //拦截、接管 gorm v2 自带日志
    })
    if err != nil {
        return nil, err //gorm 数据库驱动初始化失败
    }
    // 如果开启了读写分离，配置读数据库（resource、read、replicas）
    // 读写分离配置只
    deploy := config.MysqlConfMap[db].Deploy
    if deploy == 1 {
        ReplicasList := []gorm.Dialector{}
        for _, dsn := range getSlaveDsn(db) {
            ReplicasList = append(ReplicasList, mysql.Open(dsn))
        }
        resolverConf := dbresolver.Config{
            Replicas: ReplicasList,              //  读 操作库，查询类
            Policy:   dbresolver.RandomPolicy{}, // sources/replicas 负载均衡策略适用于
        }
        err = gormDb.Use(
            dbresolver.Register(resolverConf).
                SetConnMaxIdleTime(time.Second * 30).
                SetConnMaxLifetime(time.Second * config.MysqlConfMap[db].SlaveSetConnMaxLifetime).
                SetMaxIdleConns(config.MysqlConfMap[db].SlaveSetMaxIdleConns).
                SetMaxOpenConns(config.MysqlConfMap[db].SlaveSetMaxOpenConns))
        if err != nil {
            return nil, err
        }
    }

    // 查询没有数据，屏蔽 gorm v2 First查询不导数据包中会爆出的错误 - 推荐使用Find
    // https://github.com/go-gorm/gorm/issues/3789  此 issue 所反映的问题就是我们本次解决掉的
    _ = gormDb.Callback().Query().Before("gorm:query").Register("disable_raise_record_not_found", func(d *gorm.DB) {
        d.Statement.RaiseErrorOnNotFound = false
    })

    // 为主连接设置连接池(43行返回的数据库驱动指针)
    if rawDb, err := gormDb.DB(); err != nil {
        return nil, err
    } else {
        rawDb.SetConnMaxIdleTime(time.Second * 30)
        rawDb.SetConnMaxLifetime(config.MysqlConfMap[db].Master.SetConnMaxLifetime * time.Second)
        rawDb.SetMaxIdleConns(config.MysqlConfMap[db].Master.SetMaxIdleConns)
        rawDb.SetMaxOpenConns(config.MysqlConfMap[db].Master.SetMaxOpenConns)
        return gormDb, nil
    }
}

//获取Master主库的Dsn
func getMasterDsn(db string) string {
    Host := config.MysqlConfMap[db].Master.Host
    DataBase := config.MysqlConfMap[db].Master.DataBase
    Port := config.MysqlConfMap[db].Master.Port
    User := config.MysqlConfMap[db].Master.User
    Pass := config.MysqlConfMap[db].Master.Pass
    Charset := config.MysqlConfMap[db].Master.Charset
    return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&loc=Local", User, Pass, Host, Port, DataBase, Charset)
}

//获取Slave从库的Dsn
func getSlaveDsn(db string) []string {

    var list = []string{}
    num := config.MysqlConfMap[db].SlaveNum
    for i := 0; i < num; i++ {
        Host := config.MysqlConfMap[db].Slave[i].Host
        DataBase := config.MysqlConfMap[db].Slave[i].DataBase
        Port := config.MysqlConfMap[db].Slave[i].Port
        User := config.MysqlConfMap[db].Slave[i].User
        Pass := config.MysqlConfMap[db].Slave[i].Pass
        Charset := config.MysqlConfMap[db].Slave[i].Charset
        list = append(list, fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&loc=Local", User, Pass, Host, Port, DataBase, Charset))
    }
    return list
}

// 创建自定义日志模块，对 gorm 日志进行拦截、
func redefineLog() gormLog.Interface {
    return createCustomGormLog(
        SetInfoStrFormat("[info] %s\n"), SetWarnStrFormat("[warn] %s\n"), SetErrStrFormat("[error] %s\n"),
        SetTraceStrFormat("[traceStr] %s [%.3fms] [rows:%v] %s\n"), SetTracWarnStrFormat("[traceWarn] %s %s [%.3fms] [rows:%v] %s\n"), SetTracErrStrFormat("[traceErr] %s %s [%.3fms] [rows:%v] %s\n"))
}
